#ifndef EXECTUOR_H_
#define EXECTUOR_H_

#include "program.h"
#include "psd2.h"
#include "CuVariant.h"

struct DivisionRet
{
    Integer r0;
    Integer r1;
};

struct KaratsubaRet 
{
    Integer r0;
    BitArray r1;
    Field r2;
};

struct MulRet
{
    Integer r0;
    Boolean r1;
};

class Exectuor
{

public:
    __device__ Exectuor();
    __device__ ~Exectuor();

    __device__ void setHash(const char* epoch_hash, const char* address);
    __device__ void setProgram(Program* p);

    __device__ int rand_input(int countor);
    __device__ bool run();
    __device__ uint64_t calcHash(bool calc_public);
    __device__ void inWitness(bool flag);

    __device__ CuVariant getReg(int op);
    __device__ void setReg(int op, const CuVariant& value);

    __device__ const CuVector<Field> &newVariable();
    __device__ int newVariableCount();
    __device__ void addNewVariable(Field f);
    __device__ void addNewVariableList(const BitArray& bits, int size);

    __device__ BitArray inputToBitsLe(const CuVariant& v);
    __device__ CuVector<Field> inputToFields(const CuVariant& v);

    __device__ Boolean bit_ternary(Boolean cond, Boolean first, Boolean second);
    __device__ Boolean bit_is_equal(Boolean self, Boolean other);
    __device__ Boolean bit_is_not_equal(Boolean self, Boolean other);
    __device__ Boolean bit_and(Boolean self, Boolean other);
    __device__ Boolean bit_nand(Boolean self, Boolean other);
    __device__ Boolean bit_or(Boolean self, Boolean other);
    __device__ Boolean bit_nor(Boolean self, Boolean other);
    __device__ Boolean bit_xor(Boolean self, Boolean other);
    __device__ Boolean bit_not(Boolean self);

    __device__ Integer integer_abs_checked(const Integer& self);
    __device__ Integer integer_abs_wrapped(const Integer& self);
    __device__ Integer integer_add_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_add_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_and(const Integer& self, const Integer& other);
    __device__ Boolean integer_is_less_than(const Integer& self, const Integer& other);
    __device__ Boolean integer_is_greater_than(const Integer& self, const Integer& other);
    __device__ Boolean integer_is_less_than_or_equal(const Integer& self, const Integer& other);
    __device__ Boolean integer_is_greater_than_or_equal(const Integer& self, const Integer& other);
    __device__ Integer integer_div_checked(const Integer& self, const Integer& other);
    __device__ DivisionRet integer_unsigned_division_via_witness(const Integer& self, const Integer& other);
    __device__ Integer integer_div_wrapped(const Integer& self, const Integer& other);
    __device__ Boolean integer_is_equal(const Integer& self, const Integer& other);
    __device__ Boolean integer_is_not_equal(const Integer& self, const Integer& other);
    __device__ Integer integer_modulo(const Integer& self, const Integer& other);
    __device__ KaratsubaRet integer_karatsuba_multiply(const Integer& self, const Integer& other);
    __device__ Integer integer_mul_and_check(const Integer& self, const Integer& other);
    __device__ Integer integer_mul_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_mul_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_neg(const Integer& self);
    __device__ Integer integer_not(const Integer& self);
    __device__ Integer integer_or(const Integer& self, const Integer& other);
    __device__ MulRet integer_mul_with_flags(const Integer& self, const Integer& other);
    __device__ Integer integer_pow_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_pow_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_rem_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_rem_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_shl_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_shl_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_shr_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_shr_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_sub_checked(const Integer& self, const Integer& other);
    __device__ Integer integer_sub_wrapped(const Integer& self, const Integer& other);
    __device__ Integer integer_ternary(Boolean cond,const Integer& self, const Integer& other);
    __device__ Integer integer_xor(const Integer& self, const Integer& other);
    __device__ CuVariant integer_cassLossy(const Field& self, int type_id);

    __device__ Field field_add(const Field& self, const Field& other);
    __device__ Boolean field_is_less_than(const Field& self, const Field& other);
    __device__ Boolean field_is_greater_than(const Field& self, const Field& other);
    __device__ Boolean field_is_less_than_or_equal(const Field& self, const Field& other);
    __device__ Boolean field_is_greater_than_or_equal(const Field& self, const Field &other);
    __device__ Field field_div(const Field & self, const Field & other);
    __device__ Field field_div_unchecked(const Field & self, const Field & other);
    __device__ Field field_double(const Field & self);
    __device__ Boolean field_is_equal(const Field & self, const Field & other);
    __device__ Boolean field_is_not_equal(const Field & self, const Field & other);
    __device__ Field field_inverse(const Field & self);
    __device__ Field field_mul(const Field & self, const Field & other);
    __device__ Field field_neg(const Field & self);
    __device__ Field field_pow(const Field & self, const Field & other);
    __device__ Field field_square(const Field & self);
    __device__ Field field_square_root(const Field & self);
    __device__ Field field_sub(const Field & self, const Field & other);
    __device__ Field field_ternary(Boolean cond, const Field & self, const Field & other);
    __device__ CuVariant field_cassLossy(const Field &self,int type_id);

    __device__ Group group_ternary(Boolean cond,const Group &self, const Group &other);
    __device__ Group group_add(const Group& self, const Group& other);

protected:
    __device__ void exec(const Instruction* inst);

    __device__ void instTodo(const Instruction* inst);

    __device__ void instPed(const Instruction* inst, int ped);
    __device__ void instPsd(const Instruction* inst, int psd);
    __device__ void instBhp(const Instruction* inst, int bhp);

    __device__ void instAdd(const Instruction* inst);
    __device__ void instAddW(const Instruction* inst);

    __device__ void instSub(const Instruction* inst);
    __device__ void instSubW(const Instruction* inst);

    __device__ void instMul(const Instruction* inst);
    __device__ void instMulW(const Instruction* inst);

    __device__ void instDiv(const Instruction* inst);
    __device__ void instDivW(const Instruction* inst);

    __device__ void instPow(const Instruction* inst);
    __device__ void instPowW(const Instruction* inst);

    __device__ void instShl(const Instruction* inst);
    __device__ void instShlW(const Instruction* inst);

    __device__ void instShr(const Instruction* inst);
    __device__ void instShrW(const Instruction* inst);

    __device__ void instRem(const Instruction* inst);
    __device__ void instRemW(const Instruction* inst);

    __device__ void instAbs(const Instruction* inst);
    __device__ void instAbsW(const Instruction* inst);

    __device__ void instMod(const Instruction* inst);

    __device__ void instXor(const Instruction* inst);
    __device__ void instOr(const Instruction* inst);
    __device__ void instNor(const Instruction* inst);
    __device__ void instAnd(const Instruction* inst);
    __device__ void instNand(const Instruction* inst);

    __device__ void instInv(const Instruction* inst);
    __device__ void instNeg(const Instruction* inst);
    __device__ void instDouble(const Instruction* inst);
    __device__ void instNot(const Instruction* inst);
    __device__ void instSquare(const Instruction* inst);

    __device__ void instTernary(const Instruction* inst);

    __device__ void instGt(const Instruction* inst);
    __device__ void instGte(const Instruction* inst);
    __device__ void instLt(const Instruction* inst);
    __device__ void instLte(const Instruction* inst);
    __device__ void instEq(const Instruction* inst);
    __device__ void instNotEq(const Instruction* inst);

    __device__ void instCastLossy(const Instruction* inst);

    __device__ void to_unique_bits_le(Field f);

    Psd2 m_psd2;
    CuVariant m_regs[300];
    Program *m_program;
    
    CuVector<Field> m_newPublic;
    CuVector<Field> m_newVariable;
    CuVector<bool> m_witness;

    CuByteArray m_epoch_hash;
    CuByteArray m_address;
};

#endif
